/** @file         dm_manager.c
 *  @brief        driver mem manager 驱动内存管理器 框架实现
 *  @details      用于对物理内存进行管理，包括申请、释放、读写等
 *  @author       Schips
 *  @date         2020-08-25 09:31:34
 *  @version      v1.0
 *  @copyright    Copyright By Schips, All Rights Reserved
 *
 **********************************************************
 *
 *  @par 修改日志:
 *  <table>
 *  <tr><th>Date       <th>Version   <th>Author    <th>Description
 *  <tr><td>2020-08-25 <td>1.0       <td>Schips    <td>创建初始版本
 *  </table>
 *
 **********************************************************
 */

#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/printk.h>
#include "dm_mamanger.h"

#define SRT_HASH 0x61c88647

static linknode *mem_head = NULL;

static inline void * request_mem(size_t size)
{
    return kmalloc(size, GFP_ATOMIC);
    //return malloc(size);
}
static inline void free_mem(void * prt)
{
    kfree(prt);
    //free(prt);
    return ;
}

static inline linknode * request_list_head(void)
{
    linknode * head;

    head = request_mem(sizeof(linknode));
    if(head == NULL)
    {
        return NULL;
    }

    INIT_LIST_HEAD(&(head->list));

    return head;
}

static inline linknode * new_node(void)
{
    linknode * node;

    node = request_mem(sizeof(linknode));
    if(node == NULL)
    {
        return NULL;
    }
    INIT_LIST_HEAD(&(node->list));

    return node;
}

static inline void insert_node_to_list(linknode * head, linknode * node)
{
    if(!head || !node) return;
    list_add(&node->list, &head->list);
}

static int _char_to_lower(int c)
{
    if ((c >= 'A') && (c <= 'Z'))
        return c + ('a' - 'A');
    return c;
}

static unsigned int _str_hash(const char* str)
{
    int tmp, c = *str;
    unsigned int seed = SRT_HASH; 
    unsigned int hash = 0;

    if(!str) return SRT_HASH;

    while(*str) {
        tmp = _char_to_lower(c);
        hash = (hash ^ seed) + tmp;
        str++;
        c = *str;
    }

    return hash;
}

static int _str_match(const char *str_a, const char *str_b)
{
    int c1, c2;

    if(!str_a) return -1;
    if(!str_b) return -1;

    do {
        c1 = _char_to_lower(*str_a++);
        c2 = _char_to_lower(*str_b++);
    } while((c1 == c2) && c1);

    return c1 - c2;
}

int mem_op_w(char *name, unsigned long pos_l, const void *buf, size_t count)
{
    int i, flag = 1;
    char __iomem * va;
    char *buffer = (char *) buf;
    struct list_head *pos;
    linknode *node;
    unsigned int hash = _str_hash(name);

    if(!mem_head) return -1;
    if(!name) return -1;

    if(list_empty(&mem_head->list)) 
    {
        return -1;
    }
    // 查找效率 在 O(N)
    list_for_each(pos, &mem_head->list)
    {
        node = list_entry(pos, linknode, list);
        if(hash == node->mem_block.hash)
        {
            if(!_str_match(name, node->mem_block.name))
            {
                flag = 0;
                break;
            }
        }
    }

    if(flag) return -1; // 未找到，返回

    // 超出地址范围，返回
    if((pos_l + count) > node->mem_block.phy_mem_size) return -1;

    va = (char *) (node->mem_block.base_va+ pos_l);
    for (i = 0; i < count; ++i) {
        va[i] = buffer[i]; // write to va
    }

    return 0;
}

int mem_op_r(char *name, unsigned long pos_l, void *buf, size_t count)
{
    int i, flag = 1;
    char __iomem * va;
    char *buffer = (char *) buf;
    struct list_head *pos;
    linknode *node;
    unsigned int hash = _str_hash(name);

    if(!mem_head) return -1;
    if(!name) return -1;

    if(list_empty(&mem_head->list)) 
    {
        return -1;
    }

    // 查找效率 在 O(N)
    list_for_each(pos, &mem_head->list)
    {
        node = list_entry(pos, linknode, list);
        if(hash == node->mem_block.hash)
        {
            if(!_str_match(name, node->mem_block.name))
            {
                flag = 0;
                break;
            }
        }
    }

    if(flag) return -1; // 未找到，返回
    // 超出地址范围，返回
    if((pos_l + count) > node->mem_block.phy_mem_size) return -1;

    va = (char *) (node->mem_block.base_va+ pos_l);
    for (i = 0; i < count; ++i) {
        buffer[i] = va[i];  // read from va
    }
    return 0;
}

// 初始化单块内存
int init_mem_block(struct mem_block* m_block)
{
    int ret;
    //char * name = m_block->name;
    if(!m_block) return -1;

    m_block->res = request_mem_region(m_block->phy_mem_base, 
                                      m_block->phy_mem_size,
                                      //"Name for mem BLock");
                                      //(char*)name);
        m_block->name);
    printk(KERN_WARNING "m_block->name %s\n", m_block->name);
    if(!(m_block->res))
    {
        ret = -EBUSY;
        printk(KERN_WARNING "request_mem_region error\n");
    }

    m_block->base_va = ioremap(m_block->phy_mem_base,
                               m_block->phy_mem_size);

    if(!(m_block->base_va)){
        ret = -EBUSY;
        printk(KERN_WARNING "ioremap error\n");
        goto err;
    }

    return 0;

err : 
    if(m_block->base_va)
    {
        iounmap(m_block->base_va);
    }

    if(m_block->res)
    {
        release_mem_region(m_block->phy_mem_base, m_block->phy_mem_size);
    }

    return -EBUSY;
}

void  deinit_mem_block(struct mem_block * m_block)
{
    if(!m_block) return ;
    if(m_block->base_va)
    {
        iounmap(m_block->base_va);
    }

    if(m_block->res)
    {
        release_mem_region(m_block->phy_mem_base, m_block->phy_mem_size);
    }

    return ;
}

#if 0
void list_all(void)
{
    struct list_head *pos;
    linknode *node;

    if(!mem_head) return;

    if(list_empty(&mem_head->list)) 
    {
        return;
    }
    // 效率 在 O(N)
    list_for_each(pos, &mem_head->list)
    {
        node = list_entry(pos, linknode, list);
        printk(KERN_WARNING "%s\n", node->mem_block.name);
    }

    return ;
}

void strcp_for_dm(char *destin, char *source, unsigned n)
{
    int i;
    if(!destin) return;
    if(!source) return;

    for (i = 0; i <= BLOCK_NAME_SIZE; ++i) {
        destin[i] = source [i];
        if(i == n)            break;
        if(source[i] == '\0') break;
    }
    destin[BLOCK_NAME_SIZE - 1] = '\0';
}
#endif
// 通过名字注册内存
void __iomem* registe_mem(unsigned long phy_mem_base, unsigned long phy_mem_size, char* name)
{
    int ret;
    linknode * node;
    struct mem_block block = {
        .phy_mem_base    = phy_mem_base,
        .phy_mem_size    = phy_mem_size,
        .res             = NULL,
        .base_va         = NULL,
        .name            = name,
        .hash            = _str_hash(name),
    };
    // 不允许名字为空，因为后续的操作，如 mem_op_r, mem_op_w 需要指定名字才能够操作。
    if(!name) return NULL;
    //strcp_for_dm(block.name, name, sizeof(block.name));
    //printk(KERN_WARNING "block.name %s\n", block.name);

    ret = init_mem_block(&block);
    if(ret) 
    {
        printk(KERN_WARNING "init_mem_block error\n");
        return NULL;
    }

    if(!mem_head)
    {
        dm_manager_init();
    }

    node = new_node();
    node->mem_block = block;

    insert_node_to_list(mem_head, node);

    return block.base_va;
}

// 通过名字注销内存
void unregiste_mem(char* name)
{
    struct list_head *pos;
    linknode *node;
    unsigned int hash = _str_hash(name);

    if(!mem_head) return;
    if(!name) return;

    if(list_empty(&mem_head->list)) 
    {
        return;
    }

    // 查找效率 在 O(N)
    list_for_each(pos, &mem_head->list)
    {
        node = list_entry(pos, linknode, list);
        if(hash == node->mem_block.hash)
        {
            if(!_str_match(name, node->mem_block.name))
            {
                deinit_mem_block(&(node->mem_block));
                free_mem(node);
                break;
            }
        }
    }

    return;
}


void dm_manager_init(void)
{
    if(!mem_head)
    {
        mem_head = request_list_head();
    }

    return ;
}

void dm_manager_deinit(void)
{
    struct list_head *pos, *n;
    linknode *node;

    if(!mem_head) return;

    list_for_each_safe(pos, n, &mem_head->list)
    {
        node = list_entry(pos, linknode, list);
        deinit_mem_block(&(node->mem_block));
        free_mem(node);
    }
    free_mem(mem_head);

    return ;
}
